import http from "./http"

export class Loader<Data = any> {
    readonly api: string | ((params: any, signal: AbortSignal) => Promise<Data>)
    baseParams: any = {}
    dynamicParams?: () => object
    get data(): Data | undefined { return this._data }
    get isLoading(): boolean { return this.loadingCount > 0 }
    get error(): Error | undefined { return this._error }

    protected _data: Data | undefined = undefined
    protected _error: Error | undefined = undefined
    private _requestId: number = 0
    private abortController: AbortController| undefined = undefined
    private loadingCount = 0

    constructor(api: string | ((params: any, signal: AbortSignal) => Promise<Data>), dynamicParams?: () => object) {
        this.api = api
        this.dynamicParams = dynamicParams
    }

    protected async _load(additionalParams?: any): Promise<Data> {
        this.abortController?.abort()
        const abortCtrl = new AbortController()
        this.abortController = abortCtrl
        const requestId = this._requestId + 1
        this._requestId = requestId;
        const _params = {
            ...this.baseParams,
            ...this.dynamicParams?.(),
            ...additionalParams
        }
        this.loadingCount += 1
        let request: Promise<Data>
        if (typeof this.api === 'string') {
            request = http.post(this.api, _params, { signal: abortCtrl.signal })
        } else {
            request = this.api(_params, abortCtrl.signal)
        }
        this._error = undefined
        return new Promise<Data>(async (resolve, reject) => {
            try {
                const data = await request
                if (this._requestId !== requestId) {
                    console.log('已丢弃过期的请求数据',data)
                    return //中断已过期请求的promise
                }
                resolve(data)
            } catch (error: any) {
                if (error.isAbort) {
                    console.log('已忽略取消的请求',error)
                    return  //中断已取消请求的promise
                }
                this._error = error
                reject(error)
            } finally {
                Promise.delay(0).then(() => this.loadingCount -= 1)
            }
        })
    }

    async load(additionalParams?: any): Promise<Data> {
        this._data = await this._load(additionalParams)
        return this._data
    }

    clear() {
        this._data = undefined
        this._error = undefined
    }
}